import constants from '@/constants';
import { halt } from '@/utils/response';
import { InjectRedis } from '@liaoliaots/nestjs-redis';
import { ExecutionContext, HttpStatus, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { JwtService } from '@nestjs/jwt';
import { AuthGuard } from '@nestjs/passport';
import { Redis } from 'ioredis';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  constructor(
    private reflector: Reflector,
    private jwtService: JwtService,
    @InjectRedis()
    private redis: Redis,
  ) {
    super();
  }

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const handler = context.getHandler();
    const clazz = context.getClass();
    const isPublic = this.reflector.getAllAndOverride<boolean>(
      constants.metadatas.public,
      [handler, clazz],
    );
    if (isPublic) {
      return true;
    }
    if (!request.headers?.authorization) {
      return halt('身份认证失败', HttpStatus.UNAUTHORIZED);
    }
    if (request.url.includes('/rpa')) {
      if (request.headers.authorization !== `RPA ${constants.rpa.token}`) {
        return halt('身份认证失败', HttpStatus.UNAUTHORIZED);
      }
      return true;
    }
    const token = request.headers.authorization
      .toString()
      .replace('Bearer ', '');
    if (!token) {
      return halt('身份认证失败', HttpStatus.UNAUTHORIZED);
    }
    const payload = await this.jwtService.verifyAsync(token);
    if (request.url.includes('/admin')) {
      if (request.headers.authorization === `RPA ${constants.rpa.token}`) {
        return true;
      }
      if (payload.sub !== constants.jwt.subs.admin) {
        return halt('无操作权限', HttpStatus.FORBIDDEN);
      }
    } else {
      if (
        (payload.sub !== constants.jwt.subs.admin &&
          request.url.includes('/market')) ||
        request.url.includes('/plan') ||
        request.url.includes('/activity')
      ) {
        const res = this.jwtService.verify(token);
        const jti = await this.redis.get(`token:${res.id}`);
        if (!!jti && jti !== res.jti) {
          return halt('登录已过期', HttpStatus.UNAUTHORIZED);
        }
      }
    }

    request['user'] = payload;

    return true;
  }

  handleRequest(err, user, info) {
    // You can throw an exception based on either "info" or "err" arguments
    if (err || !user) {
      return halt('身份信息异常', HttpStatus.UNAUTHORIZED);
    }
    return user;
  }
}
